
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_WORD_LEN 10
#include "nfa.h"

int remainingStatesCount; /* count of threads that have not yet processed character */

char** getStrArr(int size, int stringLen) {
    
    int i;
    char** arr = (char**)malloc( sizeof(char*) * size );
    
    for (i = 0; i < size; i++) {
        arr[i] = (char*)malloc( sizeof(char) * stringLen );
    }
    
    return arr;

}

NFA* createNFA(void) {
    
    NFA* inputNFA;
    int i;
    
    inputNFA = (NFA*)malloc( sizeof(NFA) );
    
    /* states */
    inputNFA->statesNum = 4;
    inputNFA->states = getStrArr(inputNFA->statesNum, MAX_WORD_LEN);
    inputNFA->states[0] = "q1";
    inputNFA->states[1] = "q2";
    inputNFA->states[2] = "q3";
    inputNFA->states[3] = "q4";
    
    /* initial and final states */
    inputNFA->initStateIndex = 0;
    inputNFA->finalStatesNum = 1;
    inputNFA->finalStatesIndices = (int*)calloc(sizeof(int), inputNFA->finalStatesNum);
    inputNFA->finalStatesIndices[0] = 3;
    
    /* alphabet */
    inputNFA->alphabetSize = 3;
    inputNFA->alphabet = getStrArr(inputNFA->alphabetSize, MAX_WORD_LEN);
    inputNFA->alphabet[0] = "e";
    inputNFA->alphabet[1] = "0";
    inputNFA->alphabet[2] = "1";
    inputNFA->epsilonIndex = 0;
    
    /* transitions */
    inputNFA->transTblSize = 8; /*inputNFA->statesNum * inputNFA->alphabetSize*/
    inputNFA->transTbl = (Transition*)malloc( sizeof(Transition) * inputNFA->transTblSize );
    
    inputNFA->transTbl[0].source = 0;
    inputNFA->transTbl[0].symbol = 1;
    inputNFA->transTbl[0].destination = 0;

    inputNFA->transTbl[1].source = 0;
    inputNFA->transTbl[1].symbol = 2;
    inputNFA->transTbl[1].destination = 0;

    inputNFA->transTbl[2].source = 0;
    inputNFA->transTbl[2].symbol = 2;
    inputNFA->transTbl[2].destination = 1;

    inputNFA->transTbl[3].source = 1;
    inputNFA->transTbl[3].symbol = 0;
    inputNFA->transTbl[3].destination = 2;

    inputNFA->transTbl[4].source = 1;
    inputNFA->transTbl[4].symbol = 2;
    inputNFA->transTbl[4].destination = 2;

    inputNFA->transTbl[5].source = 2;
    inputNFA->transTbl[5].symbol = 2;
    inputNFA->transTbl[5].destination = 3;

    inputNFA->transTbl[6].source = 3;
    inputNFA->transTbl[6].symbol = 1;
    inputNFA->transTbl[6].destination = 3;

    inputNFA->transTbl[7].source = 3;
    inputNFA->transTbl[7].symbol = 2;
    inputNFA->transTbl[7].destination = 3;    
    /*
    inputNFA->transTbl[0].source = inputNFA->states[0];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[1];
    inputNFA->transTbl[0].destination = inputNFA->states[0];
    
    inputNFA->transTbl[0].source = inputNFA->states[0];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[2];
    inputNFA->transTbl[0].destination = inputNFA->states[0];
    
    inputNFA->transTbl[0].source = inputNFA->states[0];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[2];
    inputNFA->transTbl[0].destination = inputNFA->states[1];
    
    inputNFA->transTbl[0].source = inputNFA->states[1];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[0];
    inputNFA->transTbl[0].destination = inputNFA->states[2];
    
    inputNFA->transTbl[0].source = inputNFA->states[1];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[2];
    inputNFA->transTbl[0].destination = inputNFA->states[2];
    
    inputNFA->transTbl[0].source = inputNFA->states[2];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[2];
    inputNFA->transTbl[0].destination = inputNFA->states[3];
    
    inputNFA->transTbl[0].source = inputNFA->states[3];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[1];
    inputNFA->transTbl[0].destination = inputNFA->states[3];
    
    inputNFA->transTbl[0].source = inputNFA->states[3];
    inputNFA->transTbl[0].symbol = inputNFA->alphabet[2];
    inputNFA->transTbl[0].destination = inputNFA->states[3];
    */
    
    return inputNFA;
}

NextClosureStates* createNextClosureForStates(NFA* nfa) {
    
    /* identifying states with their indices */
    int i;
    
    /* create state closure arrays */
    NextClosureStates* nextClosures = (NextClosureStates*)calloc(sizeof(NextClosureStates),nfa->statesNum);
    for (i = 0; i < nfa->statesNum; i++) {
        nextClosures[i].state = i;
        nextClosures[i].nextStates = (int*)calloc(sizeof(int),nfa->statesNum);
        nextClosures[i].nextStatesSize = 0;
        nextClosures[i].closureStates = (int*)calloc(sizeof(int),nfa->statesNum);
        nextClosures[i].closureSize = 0;
    }
    
    /* check through all transitions */
    for (i = 0; i < nfa->transTblSize; i++) {
        /* check if transition is on epsilon */
        if (nfa->transTbl[i].symbol == nfa->epsilonIndex) {
            nextClosures[nfa->transTbl[i].source].closureSize++;
            nextClosures[nfa->transTbl[i].source].closureStates[nextClosures[nfa->transTbl[i].source].closureSize] = nfa->transTbl[i].destination;
        } else {
            nextClosures[nfa->transTbl[i].source].nextStatesSize++;
            nextClosures[nfa->transTbl[i].source].nextStates[nextClosures[nfa->transTbl[i].source].nextStatesSize] = nfa->transTbl[i].destination;
        }
    }
    
    return nextClosures;

}

void* parseForState(void* arg) {
    
    int i, j, k, parseStrLen;
    ThreadArg* threadArg;
    threadArg = (ThreadArg*)arg;
    
    parseStrLen = strlen(threadArg->parseStr);
    
    /* enable cancellation */
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    
    /* parse through string */
    for (i = 0; i < parseStrLen; i++) {
        
        if (threadArg->prevActiveStates[threadArg->state] > 0) {
            
            /* set destinations and their closures as active */
            for (j = 0; j < threadArg->nextClosure[threadArg->state].nextStatesSize; j++) {
                
                threadArg->nextActiveStates[ threadArg->nextClosure[threadArg->state].nextStates[j] ] = 1;
                
                for (k = 0; k < threadArg->nextClosure[ threadArg->nextClosure[threadArg->state].nextStates[j] ].closureSize; k++) {
                    threadArg->nextActiveStates[ 
                                                threadArg->nextClosure[ 
                                                                        threadArg->nextClosure[threadArg->state].nextStates[j] 
                                                                      ].closureStates[k] 
                                               ] = 1;
                }

                
            }
            
/*
            / * decrement remaining States * /
            *(threadArg->statesRemaining) -= 1;
*/
            remainingStatesCount--;
            
        }
        
        pthread_mutex_lock(threadArg->threadMutex);
        
        /* wait for release */
        while (pthread_mutex_unlock(threadArg->threadMutex) != 0) ;
        
    }

    
}


int checkIfStringInLanguage(NFA* nfa, NextClosureStates* statesClosure, char* parseStr) {
    
    int strLength;
    int i,j;
    int strAccepted;
    char symbol;
    /* int remainingStatesCount; / * count of threads that have not yet processed character * / */
    int *prevActiveStates, *nextActiveStates;
    
    pthread_t* states;
    pthread_attr_t* state_attributes;
    pthread_mutex_t* mutexArr;
    ThreadArg* argArr;
    
    /* create array of arguments for states */
    argArr = (ThreadArg*)calloc(sizeof(ThreadArg), nfa->statesNum);
    
    /* create indicator array */
    prevActiveStates = (int*)calloc(sizeof(int), nfa->statesNum);
    nextActiveStates = (int*)calloc(sizeof(int), nfa->statesNum);
    mutexArr = (pthread_mutex_t*)calloc(sizeof(pthread_mutex_t), nfa->statesNum);
    
    strLength = strlen(parseStr);
    
    prevActiveStates[nfa->initStateIndex] = 1;
    remainingStatesCount = 1;
    
    /* start threads for each state */
    states = (pthread_t*)calloc(sizeof(pthread_t), nfa->statesNum);
    state_attributes = (pthread_attr_t*)calloc(sizeof(pthread_attr_t), nfa->statesNum);
    for (i = 0; i < nfa->statesNum; i++) {
        
        argArr[i].state = i;
        argArr[i].nfa = nfa;
        argArr[i].nextClosure = statesClosure;
        argArr[i].prevActiveStates = prevActiveStates;
        argArr[i].nextActiveStates = nextActiveStates;
        argArr[i].parseStr = parseStr;
        argArr[i].statesRemaining = &remainingStatesCount;
        argArr[i].threadMutex = &mutexArr[i];
        pthread_mutex_init(&mutexArr[i], NULL); /* initialize */
        
        /* create thread */
        pthread_create( &states[i], NULL, parseForState, (void*)&argArr[i]);
        
    }
    
/*
    pthread_cond_init
*/
    
    /* start reading string */
    for (i = 0; i < strLength; i++) {
        
        for (j = 0; j < nfa->statesNum; j++) {
            printf("%d,",prevActiveStates[j]);
        }
        printf("\n");
        
        while (remainingStatesCount) ; /* loop while not all states done */
        
        /* reset */
        for (j = 0; j < nfa->statesNum; j++) {
            remainingStatesCount += nextActiveStates[j];
            prevActiveStates[j] = nextActiveStates[j];
            nextActiveStates[j] = 0;            
        }
        
        /* unlock all */
        for (j = 0; j < nfa->statesNum; j++) {
            pthread_mutex_unlock(&mutexArr[j]);
        }
        
    }
    
    /* cancel all threads */
    for (i = 0; i < nfa->statesNum; i++) {
        pthread_cancel(states[i]);
    }
    
    /* check for final states */
    strAccepted = 0;
    for (i = 0; i < nfa->finalStatesNum; i++) {
        if (prevActiveStates[nfa->finalStatesIndices[i]] == 1) {
            strAccepted = 1;
            break;
        }
    }

    if (strAccepted == 1) {
        return 1;
    } else {
        return 0;
    }
    
}







